const vscode = require('vscode')
const websocket = require('ws')
const axios = require('axios')
const path = require('path')
const statusbarItemService = require('./statusBarItemService')
const template = require('./template')

class MySet {
    constructor() {
        this.set = new Set();
    }

    add(obj) {
        const jsonStr = JSON.stringify(obj);
        this.set.add(jsonStr);
    }

    has(obj) {
        const jsonStr = JSON.stringify(obj);
        return this.set.has(jsonStr);
    }

    values() {
        return Array.from(this.set).map(jsonStr => JSON.parse(jsonStr));
    }
}

let outputChannel = null
outputChannel = vscode.window.createOutputChannel('Vue2Blue')

// 测试
// let testOutputChannel = vscode.window.createOutputChannel('vivo.hap-debugger')
// testOutputChannel.show()
// setInterval(() => {
//     testOutputChannel.appendLine("错误xxx")
// }, 1000)


let rootPath = vscode.workspace.workspaceFolders[0].uri.path
// let template_pattern = /<template>([\s\S]*?)<\/template>/
// let script_pattern = /<script>([\s\S]*?)<\/script>/
// let style_pattern = /<style[^>]*>([\s\S]*?)<\/style>/

// 当前正在处理的文件
let currentFileUri = null
// 当前正在处理的事件循环
let convertInterval = null
// 存放编译反馈
let newErrorMessageList = new MySet()
// 编译反馈是否更新
let newErrorMessageUpdateFlag = false
// 是否要处理下一个组件
let isNext = true
// 是否发送信息
let sendMessageFlag = false
// 是否监听错误输出
let isFeedback = false
// websocket是否连接服务器
let connectFlag = false
// 全部页面
let allPages = []
// 错误反馈
let feedbackIntervalCount = 0
let sendMessageCount = 0

function initVar() {
    // 当前正在处理的文件
    currentFileUri = null
    // 当前正在处理的事件循环
    convertInterval = null
    // 存放编译反馈
    newErrorMessageList = new MySet()
    // 编译反馈是否更新
    newErrorMessageUpdateFlag = false
    // 是否要处理下一个组件
    isNext = true
    // 是否发送信息
    sendMessageFlag = false
    // 是否监听错误输出
    isFeedback = false
    // 全部页面
    allPages = []
    // 错误反馈
    feedbackIntervalCount = 0
    sendMessageCount = 0
}

// vivo debugger 插件
let vivoHapDebuggerEditor = null
let capPluginName = 'vivo.hap-debugger'
// let capPluginName = 'Vue2Blue'

let ws = null
let initConnect = true

function printErrorFeedback() {
    newErrorMessageListArray = newErrorMessageList.values()
    appendLogLine("错误反馈：" + JSON.stringify(newErrorMessageListArray))
}

async function feedbackInterval() {
    newErrorMessageListArray = newErrorMessageList.values()
    appendLogLine("错误反馈：" + JSON.stringify(newErrorMessageListArray))
    if (newErrorMessageUpdateFlag || feedbackIntervalCount > 3) {
        appendLogLine(`修改记录：${newErrorMessageUpdateFlag}, 反馈次数：${feedbackIntervalCount}`)
        // 刷新错误列表，或者超过3次
        if (newErrorMessageListArray.length > 0) {
            // 有错误，继续处理
            // 错误反馈超过三次没有解决，那么跳过
            if (sendMessageCount > 3) {
                isNext = true
                sendMessageCount = 0
                appendLogLine("错误反馈超过三次没有解决，跳过")
            }
            let document = await vscode.workspace.openTextDocument(currentFileUri)
            ws.send(JSON.stringify({
                type: 'convert:feedback',
                filepath: vscode.workspace.asRelativePath(currentFileUri),
                content: document.getText(),
                error: newErrorMessageListArray
            }))
            newErrorMessageList = new MySet()
            sendMessageCount += 1
        } else {
            // 下一个页面
            isNext = true
            if (currentFileIndex >= uxFiles.length) {
                if (convertInterval) {
                    clearInterval(convertInterval)
                }
                statusbarItemService.updateStatusBarItem('Vue2Blue转换完成', 'Vue2Blue转换完成')
            }
            // vscode.window.showInformationMessage("下一页面")
        }
        feedbackIntervalCount = 0
    } else {
        feedbackIntervalCount += 1
        appendLogLine("继续监听")
        setTimeout(() => {
            feedbackInterval()
        }, 1000)
        appendLogLine("继续监听")
    }
}

function connectWebsocketServer() {
    ws = new websocket.WebSocket('ws://127.0.0.1:8061/ws')
    ws.on('open', () => {
        // appendLogLine("成功连接Vue2Blue服务")
        vscode.window.showInformationMessage("成功连接Vue2Blue服务")
        statusbarItemService.updateStatusBarItem(`Vue2Blue`, `Vue2Blue状态正常`)
        connectFlag = true
    })
    ws.on('message', (data) => {
        message = JSON.parse(data)
        if (message.type === "convert:result") {
            if (message.process != "done") {
                // appendLogLine("转换进度：" + message.process)
                // vscode.window.showInformationMessage("转换结束：" + message.process)
            } else {
                appendLogLine(`${currentFileUri.path} 转换完成，正在写入`)
                statusbarItemService.updateStatusBarItem('$(loading~spin) 等待错误反馈', '等待错误反馈')
                // 转换结果，将其写入对应文件
                let result = message.content
                // 操作vscode
                let editor = vscode.window.activeTextEditor
                editor.edit(builder => {
                    const wholeDocumentRange = new vscode.Range(
                        editor.document.positionAt(0),
                        editor.document.positionAt(editor.document.getText().length)
                    );
                    // 替换整个文档的内容
                    builder.replace(wholeDocumentRange, result);
                })
                // 触发保存事件，进而可以触发插件（语言服务器）自动编译
                isFeedback = true
                editor.document.save()
                newErrorMessageList = new MySet()
                // setTimeout(() => {
                //     getDiagnostics()
                //     newErrorMessageUpdateFlag = true
                // }, 1000)
                // 等待编译并获得反馈
                setTimeout(() => {
                    feedbackInterval()
                }, 2000)
            }
        } else if (message.type === "convert:step") {
            // vscode.window.showInformationMessage("转换开始：" + message.process)
            statusbarItemService.updateStatusBarItem(`$(loading~spin) ${message.process}转换中`, `${message.process}转换中`)
        }
    })
    ws.on('close', () => {
        if (connectFlag) {
            vscode.window.showErrorMessage("断开连接Vue2Blue服务")
            statusbarItemService.updateStatusBarItem("Vue2Blue", '断开连接Vue2Blue服务', flag = 'error')
        }
        connectFlag = false
        // 重连
        setTimeout(() => {
            connectWebsocketServer()
        }, 1000)
    })
    ws.on('error', (e) => {
        console.log(e)
        if (initConnect) {
            vscode.window.showErrorMessage("连接Vue2Blue服务失败，请检查服务是否正常。")
            statusbarItemService.updateStatusBarItem("Vue2Blue", '连接Vue2Blue服务失败，请检查服务是否正常。', flag = 'error')
        }
        initConnect = false
        ws.close()
    })
}
connectWebsocketServer()


async function getDiagnostics() {
    let diagnostics = vscode.languages.getDiagnostics(currentFileUri)
    if (diagnostics && diagnostics.length > 0) {
        for (let diagnostic of diagnostics) {
            let document = await vscode.workspace.openTextDocument(currentFileUri)
            let errorComponentCodeSection = document.getText(diagnostic.range)
            newErrorMessageList.add({
                component: '',
                message: diagnostic.message,
                code: errorComponentCodeSection
            })
        }
    }
}


/**
 * Vue文件转换为Blue ux文件
 * @param {list} uxFiles 
 */

let isRegister = false
async function vue2Blue(uxFiles, context) {
    statusbarItemService.updateStatusBarItem('$(loading~spin) Vue2Blue准备中', 'Vue2Blue准备中')
    // 初始化操作
    initVar()


    for (let uxFile of uxFiles) {
        if (uxFile.path.includes('pages')) {
            allPages.push(vscode.workspace.asRelativePath(uxFile).replace('src', ''))
        }
    }
    // 获取vivo.hap-debugger插件的editor对象
    for (let editor of vscode.window.visibleTextEditors) {
        if (editor.document.uri.scheme === 'output') {
            appendLogLine(`输出通道：${editor.document.fileName}`)
            if (editor.document.fileName.includes(capPluginName)) {
                appendLogLine(`成功获取插件${capPluginName} OutputChannel的editor对象`)
                vivoHapDebuggerEditor = editor
            }
        }
    }
    if (!isRegister) {
        // 获得代码编译反馈
        context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(async (event) => {
            // 获得插件OutputChannel的输出
            if (vivoHapDebuggerEditor && event.document === vivoHapDebuggerEditor.document) {
                if (!isFeedback) {
                    return
                }
                // console.log(event.contentChanges[0].text)
                let errorMessageList = event.contentChanges[0].text.split('\n')
                appendLogLine(`插件输出：${errorMessageList.length}, ${errorMessageList}`)
                // TODO: 因为当前编译错误信息过于复杂，规则仍未完善，暂时先忽略编译错误输出，仅收集问题诊断。
                // for (let errorMessage of errorMessageList) {
                //     if (errorMessage.trim() === '') {
                //         continue
                //     }
                //     if (errorMessage.includes('App Server')) {
                //         continue
                //     }
                //     if (errorMessage.trim().startsWith('[ERROR]') || errorMessage.trim().startsWith('[WARN]')) {
                //         // 解析插件错误提示
                //         let [logLevel, filepath, positionStr, logLevel1, ...messageTokenList] = errorMessage.split(' ')
                //         let message = messageTokenList.join(" ")
                //         // 打开错误文件
                //         let document = await vscode.workspace.openTextDocument(vscode.Uri.file(filepath))
                //         let [line, col] = positionStr.replace('@', '').split(':')
                //         // 获得出错的组件
                //         let errorPosition = new vscode.Position(parseInt(line), parseInt(col))
                //         let errorPositionLine = new vscode.Position(parseInt(line), parseInt(0))
                //         let errorComponentRange = document.getWordRangeAtPosition(errorPosition)
                //         let errorComponent = document.getText(errorComponentRange)
                //         // 查找错误部分的代码
                //         let errorComponentCodeSection = ''
                //         let textAfterOffset = document.getText(new vscode.Range(errorPositionLine, document.positionAt(document.getText().length)))
                //         let componentRegExp = new RegExp(`(${errorComponent}>|\/>)`)
                //         let match = componentRegExp.exec(textAfterOffset)
                //         if (match) {
                //             let endPositionOffset = document.offsetAt(errorPositionLine) + match.index + match[0].length
                //             errorComponentCodeSection = document.getText(new vscode.Range(errorPositionLine, document.positionAt(endPositionOffset)))
                //         }
                //         appendLogLine(`出错组件：${errorComponent}, 错误提示：${message}, 错误代码: ${errorComponentCodeSection}`)
                //         newErrorMessageList.add({
                //             component: errorComponent,
                //             message: message,
                //             code: errorComponentCodeSection
                //         })
                //     }
                // }
            }
            // 获得新的问题诊断，在其他处调用
            getDiagnostics()
            setTimeout(() => {
                newErrorMessageUpdateFlag = true
            }, 2000)
        }))
        isRegister = true
    }


    if (convertInterval) {
        clearInterval(convertInterval)
    }
    let currentFileIndex = -1
    convertInterval = setInterval(async () => {
        if (currentFileIndex > uxFiles.length) {
            clearInterval(convertInterval)
        }

        if (isNext) {
            currentFileIndex++
            if (currentFileIndex >= uxFiles.length) {
                clearInterval(convertInterval)
                statusbarItemService.updateStatusBarItem('Vue2Blue转换完成', 'Vue2Blue转换完成')
                return
            }
            let uxFile = uxFiles[currentFileIndex]
            appendLogLine(`当前正在转换的文件：${uxFile.fsPath}`)
            // 获取该文件document对象
            let document = await vscode.workspace.openTextDocument(uxFile)
            // 打开窗口
            let editor = await vscode.window.showTextDocument(document)
            document.save()
            // 转换
            currentFileUri = uxFile
            convertVue2Blue(document)
            isNext = false
            isFeedback = false
        }
    }, 500)
}

async function convertVue2Blue(document) {
    if (!connectFlag) {
        vscode.window.showErrorMessage("Vue2Blue服务断开，无法进行转换，请检查服务状态。")
        statusbarItemService.updateStatusBarItem(`Vue2Blue`, `Vue2Blue服务断开，无法进行转换，请检查服务状态。`, flag = 'error')
        // appendLogLine("Vue2Blue服务断开，无法进行转换，请检查服务状态。")
        return
    }
    statusbarItemService.updateStatusBarItem('$(loading~spin) Vue2Blue开始转换', `Vue2Blue开始转换, 文件${currentFileUri.path}`)
    console.log(vscode.workspace.asRelativePath(currentFileUri))
    ws.send(JSON.stringify({
        type: 'convert:start',
        filepath: vscode.workspace.asRelativePath(currentFileUri),
        content: document.getText(),
        extra: {
            pages: allPages
        }
    }))
    sendMessageFlag = true
    newErrorMessageUpdateFlag = false
}

/**
 * 添加一条输出日志
 * @param {string} message 
 */
function appendLogLine(message) {
    if (outputChannel) {
        outputChannel.appendLine(message)
    }
}

/**
 * 备份原始文件
 */
async function backAndInitProject(files) {
    statusbarItemService.updateStatusBarItem('Vue2Blue项目初始化中', 'Vue2Blue项目初始化中')
    let newFiles = []
    for (let file of files) {
        // 创建ux文件
        if (file.path.endsWith('.vue')) {
            // 规范
            let newFileDirname = path.basename(file.path, '.vue')
            newFileDirname = newFileDirname.charAt(0).toUpperCase() + newFileDirname.slice(1)
            let newFilepath = ''
            if (path.dirname(file.path).endsWith('components')) {
                newFilepath = path.join(rootPath, 'src', 'pages', 'components', newFileDirname, 'index.ux')
            } else {
                newFilepath = path.join(rootPath, 'src', 'pages', newFileDirname, 'index.ux')
            }
            try {
                await vscode.workspace.fs.copy(file, vscode.Uri.file(newFilepath))
            }
            catch (e) {
                // console.log(e.message)
            }
        }
        // 创建ux文件所需要的js模块
        if (file.path.endsWith('.js') && path.dirname(file.path).endsWith('components')) {
            // 组件内的工具js文件
            let newJsFilename = path.basename(file.path)
            let newFileDirname = path.basename(file.path, '.vue')
            newFileDirname = newFileDirname.charAt(0).toUpperCase() + newFileDirname.slice(1)
            newFilepath = path.join(rootPath, 'src', 'pages', 'components', newFileDirname, newJsFilename)
        }

        // 跳过README.md文件
        if (path.basename(file.path).toLowerCase() === 'readme.md') {
            newFiles.push(file)
            continue
        }
        // 跳过ux文件
        if (file.path.endsWith('.ux')) {
            newFiles.push(file)
            continue
        }

        // 移动到备份文件夹内
        let relativePath = vscode.workspace.asRelativePath(file.path)
        let filepathBack = `${rootPath}/.vue2blue/back/${relativePath}`
        try {
            await vscode.workspace.fs.rename(file, vscode.Uri.file(filepathBack), {
                overwrite: false
            })
            newFiles.push(vscode.Uri.file(filepathBack))
        }
        catch (e) {
            // console.log(e.message)
        }
    }
    initBlueOSProject(newFiles)
}

/**
 * 应用Jinja模板
 */
function applyJinjiaTemplate(jinjaTemplate, data) {
    return jinjaTemplate.replace(/{{\s*([^}]+)\s*}}/g, (match, key) => {
        return data[key.trim()] || ''
    })
}

/**
 * 创建文件，封装vscode.workspace.fs.writeFile
 */
function createFile(filepath, content, overwrite = false, silent = true) {
    vscode.workspace.fs.writeFile(vscode.Uri.file(filepath), Buffer.from(content), {
        create: true,
        overwrite: overwrite
    })
        .then(() => {
            if (!silent) {
                appendLogLine(`创建${filepath}成功`)
            }
        })
        .catch(e => {
            if (!silent) {
                appendLogLine(`创建${filepath}失败: ${e}`)
            }
        })
}

/**
 * 创建模板文件
 */
async function initBlueOSProject(files) {
    // 创建app.ux
    let appUxFilepath = path.join(rootPath, 'src', 'app.ux')
    createFile(appUxFilepath, template.appUxFileTemplate, true, false)
    // 创建preview.json
    let prewviewJsonFilepath = path.join(rootPath, '.preview.json')
    createFile(prewviewJsonFilepath, template.previewJsonFileTemplate, true, false)
    // 创建jsconfig.json
    let jsconfigJsonFilepath = path.join(rootPath, 'jsconfig.json')
    createFile(jsconfigJsonFilepath, template.jsconfigJsonFileTemplate, true, false)
    // 创建package.json
    let packageJsonFilepath = path.join(rootPath, 'package.json')
    let projectName = path.basename(rootPath)
    let readmeContent = ''
    // 查找readme文件
    for (let file of files) {
        if (path.basename(file.path).toLowerCase() === 'readme.md') {
            let document = await vscode.workspace.openTextDocument(file)
            readmeContent = document.getText()
            break
        }
    }
    let descriptionResponse = null
    try {
        descriptionResponse = await axios.post('http://127.0.0.1:8061/description',
            {
                projectName: projectName,
                description: readmeContent ? readmeContent : projectName
            },
            {
                timeout: 300000  // 5分钟
            }
        )
    }
    catch {
        appendLogLine('获取项目描述失败，使用默认值, 请检查Vue2Blue服务是否正常。')
    }
    let packageJsonFileData = null
    if (descriptionResponse) {
        packageJsonFileData = {
            "appName": `"${descriptionResponse.data.projectName}"`,
            "appDescription": `"${descriptionResponse.data.description}"`
        }
    } else {
        packageJsonFileData = {
            "appName": `"${projectName}"`,
            "appDescription": `"${projectName}"`
        }
    }

    let packageJsonFileTemplate = applyJinjiaTemplate(template.packageJsonFileJinjaTemplate, packageJsonFileData)
    createFile(packageJsonFilepath, packageJsonFileTemplate, true, false)
    // 创建selfCloseInputTag.js
    let selfCloseInputTagJsFilepath = path.join(rootPath, 'scripts', 'selfCloseInputTag.js')
    createFile(selfCloseInputTagJsFilepath, template.selfCloseInputTagJsFileTemplate, true, false)
    // 创建genIndex.js
    let genIndexJsFilepath = path.join(rootPath, 'scripts', 'gen', 'index.js')
    createFile(genIndexJsFilepath, template.genIndexJsFileTemplate, true, false)
    // 创建genTemplate.ux
    let genTemplateUxFilepath = path.join(rootPath, 'scripts', 'gen', 'template.ux')
    createFile(genTemplateUxFilepath, template.genTemplateUxFileTemplate, true, false)
    // 创建.prettierignore
    let prettierIgnoreFilepath = path.join(rootPath, '.prettierignore')
    createFile(prettierIgnoreFilepath, template.prettierignoreFileTemplate, true, false)
    // 创建.gitignore
    let gitignoreFilepath = path.join(rootPath, '.gitignore')
    createFile(gitignoreFilepath, template.gitignoreFileTemplate, true, false)

    // 创建manifest.json
    let displayPages = {}
    let routerPages = {}
    let entryPage = ''
    // 1. 找到入口组件
    let entryComponent = ''
    for (let file of files) {
        // 根据main.js文件判断入口页面
        if (path.basename(file.path) === 'main.js') {
            let document = await vscode.workspace.openTextDocument(file)
            let content = document.getText()
            let pattern = /render:.*\((.*)\)/
            let match = pattern.exec(content)
            if (match) {
                entryComponent = match[1]
            } else {
                console.error('找不到入口组件')
            }
        }
    }
    console.log(entryComponent)

    for (let file of files) {
        if (file.path.endsWith('.vue')) {
            let newFileDirname = path.basename(file.path, '.vue')
            newFileDirname = newFileDirname.charAt(0).toUpperCase() + newFileDirname.slice(1)
            if (path.dirname(file.path).endsWith('components')) {
                newFileDirname = `components/${newFileDirname}`
            }
            displayPages[`pages/${newFileDirname}`] = {
                titleBarText: `${newFileDirname}`
            }
            routerPages[`pages/${newFileDirname}`] = {
                component: "index"
            }

            // 判断当前组件是否是入口组件，找到entryPage
            let pattern = /name:.*["'](.*)["']/
            let document = await vscode.workspace.openTextDocument(file)
            let content = document.getText()
            let match = pattern.exec(content)
            if (match && match[1].toLowerCase() === entryComponent.toLowerCase()) {
                entryPage = `pages/${newFileDirname}`
            }
        }
    }
    let manifestJsonFilepath = path.join(rootPath, 'src', 'manifest.json')
    let manifestJsonFileTemplate = applyJinjiaTemplate(template.manifestJsonFileJinjaTemplate, {
        displayPages: JSON.stringify(displayPages),
        routerPages: JSON.stringify(routerPages),
        entryPage: `"${entryPage}"`,
        appName: packageJsonFileData.appName
    })
    createFile(manifestJsonFilepath, manifestJsonFileTemplate, true, false)

    // 创建assets/styles下的文件
    let assetsStylesMixinsLessFilepath = path.join(rootPath, 'src', 'assets', 'styles', 'mixins.less')
    createFile(assetsStylesMixinsLessFilepath, template.assetsStylesMixinsLessFileTemplate, true, false)
    let assetsStylesStyleLessFilepath = path.join(rootPath, 'src', 'assets', 'styles', 'style.less')
    createFile(assetsStylesStyleLessFilepath, template.assetsStylesStyleLessFileTemplate, true, false)
    let assetsStylesVariablesLessFilepath = path.join(rootPath, 'src', 'assets', 'styles', 'variables.less')
    createFile(assetsStylesVariablesLessFilepath, template.assetsStylesVariablesLessFileTemplate, true, false)
    // 创建helper下的文件
    let helperAjaxJsFilepath = path.join(rootPath, 'src', 'helper', 'ajax.js')
    createFile(helperAjaxJsFilepath, template.helperAjaxJsFileTemplate, true, false)
    let helperUtilsJsFilepath = path.join(rootPath, 'src', 'helper', 'utils.js')
    createFile(helperUtilsJsFilepath, template.helperUtilsJsFileTemplate, true, false)
    let helperApisExampleJSFilepath = path.join(rootPath, 'src', 'helper', 'apis', 'example.js')
    createFile(helperApisExampleJSFilepath, template.helperApisExampleJSFileTemplate, true, false)

    statusbarItemService.updateStatusBarItem('Vue2Blue项目初始化完成', 'Vue2Blue项目初始化完成')
    setTimeout(() => {
        statusbarItemService.updateStatusBarItem('Vue2Blue', 'Vue2Blue')
    }, 1000)
}


module.exports = {
    vue2Blue,
    appendLogLine,
    backAndInitProject,
    printErrorFeedback
}